home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / mewin10s.zip / MSWSYS.C < prev    next >
C/C++ Source or Header  |  1992-04-10  |  32KB  |  1,034 lines

  1. /* The routines in this file provide support for various OS-related
  2.    functions under the Microsoft Windows environment on an IBM-PC or
  3.    compatible computer.
  4.  
  5.    Must be compiled with Borland C++ 2.0.
  6.  
  7.    It should not be compiled if the WINDOW_MSWIN symbol is not set */
  8.  
  9. #include    "estruct.h"
  10. #include    "elang.h"
  11. #include    <stdio.h>
  12. #include    <time.h>
  13. #include    "eproto.h"
  14. #include    "edef.h"
  15.  
  16. #define        OEMRESOURCE    1   /* to have access to OBM_CLOSE from windows.h */
  17. #include    "mswin.h"
  18.  
  19. #define MAXPARAM    10      /* max command line parameters */
  20. #define TXTSIZ      NFILEN  /* all purpose string length */
  21. #define T_SLEEP      1      /* Sleep timer ID */
  22.  
  23. extern char *itoa (int value, char *string, int radix);
  24. extern int  putenv (const char *name);
  25.  
  26. /* message codes for EmacsBroadcast */
  27. #define EMACS_BROADCAST 0x2000
  28. #define EMACS_STARTING  0x2001
  29. #define EMACS_ENDING    0x2010
  30.  
  31. /* variables */
  32. static char     *argv[MAXPARAM];
  33. static int      argc;
  34.  
  35. static CATCHBUF ExitCatchBuf;
  36.  
  37. static int  Sleeping = 0;           /* flag for TakeANap() */
  38. static int  TimeSlice;
  39. static BOOL LongOperation = FALSE;  /* for longop() and GetCharacter() */
  40. static BOOL HourglassOn = FALSE;    /* between SetHourglass & UpdateCursor */
  41. static BOOL SystemModal = FALSE;    /* indicates that the Yes/No message
  42.                        box should be system-modal */
  43.  
  44. static int BroadcastVal;    /* used by the EmacsBroadcast function */
  45.  
  46. static FARPROC  MDIClientProc;
  47.  
  48. /* prototypes */
  49. static void near pascal MessageLoop (BOOL WaitMode);
  50. static BOOL near pascal UpdateCursor (HWND hWnd, WORD wParam, DWORD lParam);
  51. static void near pascal SetHourglass (BOOL hg);
  52.  
  53. /* timeset: return a system-dependant time string */
  54. /* =======                                        */
  55.  
  56. char * PASCAL timeset (void)
  57. {
  58.     long        Time;
  59.     struct tm   *T;
  60.     static char section [] = "intl";
  61.     static int  iDate, iTime;
  62.     static char sDate[2], sTime[2], sAMPM[2][5];
  63.     static char buf [TXTSIZ];
  64.     int         day, month, year, hour;
  65.  
  66.     time (&Time);
  67.     T = localtime (&Time);
  68.     iDate = GetProfileInt (section, "iDate", 0);
  69.     iTime = GetProfileInt (section, "iTime", 0);
  70.     GetProfileString (section, "sDate", "/", sDate, 2);
  71.     GetProfileString (section, "sTime", ":", sTime, 2);
  72.     GetProfileString (section, "s1159", "AM", sAMPM[0], 5);
  73.     GetProfileString (section, "s2359", "PM", sAMPM[1], 5);
  74.     day = T->tm_mday;
  75.     month = T->tm_mon + 1;
  76.     year = T->tm_year % 100;
  77.     hour = T->tm_hour;
  78.     wsprintf (buf, "%02d%s%02d%s%02d %02d%s%02d%s%02d %s",
  79.           iDate==1 ? day : iDate==2 ? year : month, sDate,
  80.           iDate==1 ? month : iDate==2 ? month : day, sDate,
  81.           iDate==1 ? year : iDate==2 ? day : year,
  82.           iTime==1 ? hour : (hour % 12) ? (hour % 12) : 12,
  83.           sTime, T->tm_min, sTime, T->tm_sec,
  84.           iTime==1 ? "" : sAMPM [hour / 12]);
  85.     return buf;
  86. } /* timeset */
  87.  
  88. /* longop:    to be called regularly while a long operation is in progress */
  89. /* ========                                                                */
  90.  
  91. PASCAL longop (int f)
  92.  
  93. /* f is TRUE to set long operation status and FALSE to reset that status */
  94. /* when a long operation is signaled at least twice, the hourglass
  95.    cursor comes up */
  96. /* While the long operation is in progress, this function runs the
  97.    message loop approximately every 100 ms to yield to other
  98.    applications and allow a reduced set of commands (among which quit)
  99.    to be input by the user */
  100. {
  101.     static DWORD    LastYield;  /* time of last yield in milliseconds */
  102.     
  103.     if (f) {
  104.         if (!LongOperation) {
  105.             LongOperation = TRUE;
  106.             LastYield = GetCurrentTime ();
  107.         }
  108.     else {  /* 2nd (or more) call in a row, let's yield if enough
  109.            time has elapsed */
  110.         DWORD   Time;
  111.  
  112.         if ((Time = GetCurrentTime ()) - LastYield >= TimeSlice) {
  113.         SetHourglass (TRUE);
  114.             LastYield = Time;
  115.             MessageLoop (FALSE);
  116.         }
  117.     }
  118.     }
  119.     else {
  120.     if (LongOperation) {
  121.         LongOperation = FALSE;
  122.         SetHourglass (FALSE);
  123.     }
  124.     }
  125. } /* longop */
  126.  
  127. /* mlyesno: ask a yes/no question */
  128. /* =======                        */
  129.  
  130. PASCAL mlyesno (char *prompt)
  131.  
  132. /* This function replaces the mlyesno from input.c. Instead of asking a
  133.    question on the message line, it pops up a message box */
  134. {
  135.     if (MessageBox (hFrameWnd, prompt, ProgName,
  136.                     MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION |
  137.             (SystemModal ? MB_SYSTEMMODAL : 0)) == IDYES) return TRUE;
  138.     return FALSE;
  139. } /* mlyesno */
  140.  
  141. /* mlabort: display a serious error message (proposes abort) */
  142. /* =======                                                   */
  143.  
  144. PASCAL mlabort (char *s)
  145. {
  146.     char    text[NSTRING];  /* hopefully sufficient! */
  147.     
  148.     while (*s == '%') s++;  /* remove those strange % signs in some messages */
  149.     strcpy (text, s);
  150.     strcat (text, "\tAbort ");
  151.     strcat (text, ProgName);
  152.     strcat (text, " ?");
  153.     if (MessageBox (hFrameWnd, text, NULL,
  154.                     MB_YESNO | MB_DEFBUTTON2 |
  155.                     MB_ICONHAND | MB_APPLMODAL) == IDYES) {
  156.         eexitval = -1;
  157.         Throw (ExitCatchBuf, ABORT);
  158.      }
  159. } /* mlabort */
  160.  
  161. /* WinInit: all the window initialization crap... */
  162. /* =======                                        */
  163.  
  164. BOOL far pascal WinInit (LPSTR lpCmdLine, int nCmdShow)
  165.  
  166. /* returns FALSE if failed init */
  167. {
  168.     WNDCLASS    wc;
  169.     HMENU       hSysMenu;
  170.     char        FrameClassName [TXTSIZ];
  171.     char        text [TXTSIZ];
  172.     POINT       InitPos;
  173.     int         Colors;
  174.     int         i;
  175.     static char HomeDir [NFILEN] = "HOME=";
  176.  
  177.     InitializeFarStorage ();
  178.     WinFlags = GetWinFlags ();
  179.  
  180.     i = GetModuleFileName (hEmacsInstance, text, NFILEN);
  181.     for (; i >= 0; i--) {
  182.         if (text[i] == '\\') {
  183.             text[i] = '\0';     /* transform file name into dir name */
  184.             break;
  185.         }
  186.     }
  187.     if (i > 0) {
  188.         strcat (HomeDir, text);
  189.         putenv (HomeDir);   /* set HOME environment var to point to
  190.                    MicroEMACS executable directory */
  191.     }
  192.  
  193.     HelpEngineFile[0] = '\0';
  194.  
  195.     /*-read the profile */
  196.     TimeSlice = GetProfileInt (ProgName, "TimeSlice", 100);
  197.     Colors = GetProfileInt (ProgName, "Colors", 0);
  198.     GetProfileString (ProgName, "HelpFile", "mewin.hlp", text, TXTSIZ);
  199.     MainHelpFile = copystr (text);
  200.     MainHelpUsed = FALSE;
  201.  
  202.     GetProfileString (ProgName, "InitialSize", "", text, TXTSIZ);
  203.     strlwr (text);
  204.     if (strstr (text, "optimize")) {
  205.     InitPos.x = InitPos.y = 0;
  206.     }
  207.     else {
  208.     InitPos.x = InitPos.y = CW_USEDEFAULT;
  209.     }
  210.     if (nCmdShow == SW_SHOWNORMAL) {
  211.     if (strstr (text, "maximize")) {
  212.         nCmdShow = SW_SHOWMAXIMIZED;
  213.     }
  214.     else if (strstr (text, "minimize") || strstr (text, "icon")) {
  215.         nCmdShow = SW_SHOWMINNOACTIVE;
  216.     }
  217.     }
  218.  
  219.     /*-Register the frame window class */
  220.     strcpy (FrameClassName, ProgName);
  221.     strcat (FrameClassName, "_frame");
  222.     wc.style        = CS_HREDRAW | CS_VREDRAW;
  223.     wc.lpfnWndProc  = FrameWndProc;
  224.     wc.cbClsExtra   = 0;
  225.     wc.cbWndExtra   = FRMWNDEXTRA;
  226.     wc.hInstance    = hEmacsInstance;
  227.     wc.hIcon        = LoadIcon (hEmacsInstance, "AppIcon");
  228.     wc.hCursor      = NULL;
  229.     wc.hbrBackground= COLOR_WINDOW + 1;
  230.     wc.lpszMenuName = "Menu";
  231.     wc.lpszClassName= FrameClassName;
  232.     RegisterClass (&wc);
  233.  
  234.     /*-Register the MDI child (screen) window class */
  235.     strcpy (text, ProgName);
  236.     strcat (text, "_screen");
  237.     ScreenClassName = copystr(text);
  238.     wc.style        = CS_HREDRAW | CS_VREDRAW;
  239.     wc.lpfnWndProc  = ScrWndProc;
  240.     wc.cbClsExtra   = 0;
  241.     wc.cbWndExtra   = SCRWNDEXTRA;
  242.     wc.hInstance    = hEmacsInstance;
  243.     wc.hIcon        = LoadIcon (hEmacsInstance, "ScreenIcon");
  244.     wc.hCursor      = NULL;
  245.     wc.hbrBackground= COLOR_WINDOW + 1;
  246.     wc.lpszMenuName = NULL;
  247.     wc.lpszClassName= ScreenClassName;
  248.     RegisterClass (&wc);
  249.  
  250.     /*-Create the frame window */
  251.     hFrameWnd = CreateWindow (FrameClassName,       /* class */
  252.                   ProgName,             /* title */
  253.                   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  254.                   InitPos.x,            /* positions */
  255.                   InitPos.y,
  256.                   CW_USEDEFAULT,        /* dimensions */
  257.                   CW_USEDEFAULT,
  258.                   NULL,                 /* parent handle */
  259.                   NULL,                 /* menu */
  260.                   hEmacsInstance,
  261.                   NULL);
  262.  
  263.     if (hFrameWnd == 0) return FALSE;
  264.  
  265.     if (Colors == 0) {
  266.         /* try to detect monochrome displays */
  267.     HDC     hDC;
  268.  
  269.     hDC = GetDC (hFrameWnd);
  270.         ColorDisplay = (GetDeviceCaps (hDC, NUMCOLORS) > 2);
  271.     ReleaseDC (hFrameWnd, hDC);
  272.     }
  273.     else {
  274.         ColorDisplay = (Colors > 2);
  275.     }
  276.  
  277.     ShowWindow (hFrameWnd, nCmdShow);
  278.  
  279.     hScreenCursor = LoadCursor (hEmacsInstance, "ScreenCursor");
  280.     hTrackCursor = hScreenCursor;
  281.     hNotQuiescentCursor = LoadCursor (hEmacsInstance,
  282.                                       "NotQuiescentCursor");
  283.     hHourglass = LoadCursor (NULL, IDC_WAIT);
  284.  
  285.     in_init ();                 /* sets up the input stream */
  286.  
  287.     argv [0] = ProgName;        /* dummy program name */
  288.     {
  289.     register char   *s;
  290.  
  291.     argc = 1;
  292.     s = copystr (lpCmdLine);
  293.     while (*s != '\0') {
  294.         argv[argc] = s;
  295.         if (++argc >= MAXPARAM) goto ParsingDone;
  296.         while (*++s != ' ') if (*s == '\0') goto ParsingDone;
  297.         *s = '\0';
  298.         while (*++s == ' ');
  299.     }
  300.     }
  301. ParsingDone:
  302.     return TRUE;
  303. } /* WinInit */
  304.  
  305. /* SetFrameCaption: sets the frame window's text according to the app Id */
  306. /* ===============                                                       */
  307.  
  308. static void pascal near SetFrameCaption (void)
  309. {
  310.     char    text[sizeof(PROGNAME)+19];
  311.     char    *t;
  312.     int     Id;
  313.  
  314.     strcpy (text, ProgName);
  315.     Id = GetWindowWord (hFrameWnd, GWW_FRMID);
  316.     if (Id) {
  317.     for (t = text; *t != '\0'; t++) ;   /* look for the end of text */
  318.     *t++ = ' ';
  319.     *t++ = '#';
  320.     itoa (Id, t, 10);   /* append the App Id */
  321.     }
  322.     SetWindowText (hFrameWnd, text);
  323. } /* SetFrameCaption */
  324.  
  325. /* BroadcastEnumProc:   used by EmacsBroadcast */
  326. /* =================                           */
  327. BOOL EXPORT far pascal BroadcastEnumProc (HWND hWnd, DWORD lParam)
  328. {
  329.     if ((hWnd != LOWORD(lParam)) &&
  330.     (GetClassLong (hWnd, GCL_WNDPROC) ==  (DWORD)FrameWndProc)) {
  331.         if (HIWORD(lParam) != 0) {
  332.             PostMessage (hWnd, WM_COMMAND, EMACS_BROADCAST, lParam);
  333.         }
  334.         if (HIWORD(lParam) == EMACS_STARTING) {
  335.             /*-compute highest application Id */
  336.             int     Id;
  337.  
  338.             Id = GetWindowWord (hWnd, GWW_FRMID);
  339.             if (Id == 0) Id = 1;
  340.             ++Id;
  341.             BroadcastVal = max(BroadcastVal, Id);
  342.     }
  343.     else {
  344.         /*-compute number of applications */
  345.         BroadcastVal++;
  346.     }
  347.     }
  348.     return TRUE;
  349. } /* BroadcastEnumProc */
  350.  
  351. /* EmacsBroadcast:  send a broadcast message to all Emacs applications */
  352. /* ==============                                                      */
  353.  
  354. static int pascal near  EmacsBroadcast (WORD MsgCode, HWND hWnd)
  355.  
  356. /* If MsgCode is not zero, the broadcast is sent as a WM_COMMAND to all
  357.    the Emacs frame windows, except the one specified by hWnd. The wParam
  358.    of that message is EMACS_BROADCAST, the HIWORD of lParam is set to
  359.    MsgCode and the LOWORD of lParam is set to hWnd.
  360.  
  361. The value returned by this function depends on MsgCode:
  362.  
  363.  - If MsgCode is EMACS_STARTING, the function returns the Id assigned to
  364.    the calling Emacs instance: 0 if it is alone or 1 plus the highest Id
  365.    found, discounting the instance identified by hWnd (note that for
  366.    this purpose, an instance that has an Id of 0 is equivalent to a
  367.    "highest Id" of 1).
  368.  
  369.  - If MsgCode is EMACS_ENDING or 0, the function returns the number of
  370.    emacs instances found, not counting the one identified by hWnd.
  371.  
  372. */
  373. {
  374.     FARPROC ProcInstance;
  375.  
  376.     ProcInstance = MakeProcInstance ((FARPROC)BroadcastEnumProc,
  377.                      hEmacsInstance);
  378.     BroadcastVal = 0;
  379.     EnumWindows (ProcInstance, MAKELONG(hWnd, MsgCode));
  380.     FreeProcInstance (ProcInstance);
  381.     return BroadcastVal;
  382. } /* EmacsBroadcast */
  383.  
  384. /* MDIClientSubProc:    Subclassing window proc for the MDI Client window */
  385. /* ================                                                       */
  386.  
  387. LONG EXPORT far pascal MDIClientSubProc (HWND hWnd, WORD wMsg, WORD wParam,
  388.                          LONG lParam)
  389. {
  390.     switch (wMsg) {
  391.     case WM_SETCURSOR:
  392.     if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
  393.     goto DefaultProc;
  394.  
  395.     case WM_KEYDOWN:
  396.     case WM_SYSKEYDOWN:
  397.     case WM_CHAR:
  398.     case WM_SYSCHAR:
  399.     if (EatKey (wMsg, wParam, lParam)) break;
  400.         goto DefaultProc;
  401.  
  402.     case WM_PARENTNOTIFY:
  403.         if (notquiescent) break;    /* blocks mouse selection of an MDI
  404.                        child (except on caption clicks!)
  405.                        */
  406.         goto DefaultProc;
  407.         
  408.     default:
  409. DefaultProc:
  410.     return CallWindowProc (MDIClientProc, hWnd, wMsg, wParam, lParam);
  411.     }
  412.     return 0L;
  413. } /* MDIClientSubProc */
  414.  
  415. /* FrameInit:   Frame window's WM_CREATE */
  416. /* =========                             */
  417.  
  418. void far pascal FrameInit (CREATESTRUCT *cs)
  419. {
  420.     RECT    Rect;
  421.     CLIENTCREATESTRUCT  ccs;
  422.     HMENU   hMenu;
  423.  
  424.     /*-advertise our birth to other Emacs instances and set the frame's
  425.        caption with an Id if necessary */
  426.     SetWindowWord (hFrameWnd, GWW_FRMID,
  427.                    EmacsBroadcast (EMACS_STARTING, hFrameWnd));
  428.     SetFrameCaption ();
  429.  
  430.     /*-add "optimize" to system menu */
  431.     hMenu = GetSystemMenu (hFrameWnd, 0);
  432.     InsertMenu (hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED, SC_OPTIMIZE,
  433.                 "&Optimize");
  434.     
  435.     /*-initialize fonts and Cell Metrics */
  436.     FontInit ();
  437.  
  438.     /*-Create the MDI Client window */
  439.     hMDIClientWnd = NULL;   /* the NULL initial value allows
  440.                    DefFrameProc to behave properly if
  441.                    FrameInit fails */
  442.     ccs.hWindowMenu = GetScreenMenuHandle ();
  443.     ccs.idFirstChild = IDM_FIRSTCHILD;
  444.     GetClientRect (hFrameWnd, &Rect);
  445.     hMDIClientWnd = CreateWindow ("MDICLIENT",      /* class */
  446.                   NULL,             /* title */
  447.                   WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL |
  448.                       WS_HSCROLL | WS_VISIBLE,  /* style */
  449.                       0,                   /* positions */
  450.                   0,
  451.                   Rect.right,          /* dimensions */
  452.                   Rect.bottom - EmacsCM.MLHeight,
  453.                   hFrameWnd,           /* parent handle */
  454.                   NULL,                /* menu */
  455.                   hEmacsInstance,
  456.                   (LPSTR) (LPCLIENTCREATESTRUCT) &ccs);
  457.     if (hMDIClientWnd) {
  458.         /* we subclass the MDIClient */
  459.         FARPROC ProcInstance;
  460.  
  461.         MDIClientProc = (FARPROC)GetWindowLong (hMDIClientWnd, GWL_WNDPROC);
  462.         ProcInstance = MakeProcInstance ((FARPROC)MDIClientSubProc,
  463.                          hEmacsInstance);
  464.         SetWindowLong (hMDIClientWnd, GWL_WNDPROC, ProcInstance);
  465.     }
  466. } /* FrameInit */
  467.  
  468. /* CloseEmacs:   handle WM_CLOSE of WM_QUERYENDSESSION messages */
  469. /* ==========                                                   */
  470.  
  471. static BOOL near pascal CloseEmacs (WORD wMsg)
  472.  
  473. /* returns TRUE if emacs should exit */
  474. {
  475.     if (eexitflag) return TRUE;     /* emacs has already quited */
  476.     if (wMsg == WM_QUERYENDSESSION) SystemModal = TRUE;
  477.     if (fbusy != FWRITING) {   /* no file write in progress */
  478.         quit (FALSE, 0);    /* check if it's ok with emacs to terminate */
  479.     }
  480.     else {                  /* there is, indeed a file write in progress
  481.         MessageBeep (0);    /* wake the user up! */
  482.         if (MessageBox (hFrameWnd, TEXT333,
  483.                             /* "File write in progress. Quit later!" */
  484.                         ProgName,
  485.                         MB_OKCANCEL | MB_ICONSTOP |
  486.                 (SystemModal ? MB_SYSTEMMODAL : 0)) == IDCANCEL) {
  487.             quit (TRUE, 0); /* give up! */
  488.         }
  489.     }
  490.     if (wMsg == WM_QUERYENDSESSION) SystemModal = FALSE;
  491.     if (eexitflag) {        /* emacs agrees to quit */
  492.         ClipboardCleanup ();    /* close clipboard if we had it open */
  493.         if (fbusy != FALSE) ffclose (); /* cleanup file IOs */
  494.         return TRUE;
  495.     }
  496.     update (TRUE);          /* restore the caret that quit() moved into
  497.                    the message line */
  498.     return FALSE;        /* we refuse to die! */
  499. } /* CloseEmacs */
  500.  
  501. /* ScrWndProc:  MDI child (screen) window function */
  502. /* ==========                                      */
  503. LONG EXPORT far pascal ScrWndProc (HWND hWnd, WORD wMsg, WORD wParam,
  504.                    LONG lParam)
  505. {
  506.     switch (wMsg) {
  507.  
  508.     case WM_CREATE:
  509.     {   /*-init WindowWords for ScrReSize function */
  510.         RECT    Rect;
  511.  
  512.         GetClientRect (hWnd, &Rect);
  513.         SetWindowWord (hWnd, GWW_SCRCX, Rect.right);
  514.         SetWindowWord (hWnd, GWW_SCRCY, Rect.bottom);
  515.     }
  516.     {   /*-setup the stuff for display.c */
  517.         SCREEN  *sp;
  518.  
  519.         sp = (SCREEN*)(((MDICREATESTRUCT*)(((CREATESTRUCT*)lParam)->
  520.                            lpCreateParams))->lParam);
  521.         SetWindowLong (hWnd, GWL_SCRPTR, (DWORD)sp);
  522.         sp->s_drvhandle = hWnd;
  523.     }
  524.     goto DefaultProc;
  525.     
  526.     case WM_DESTROY:
  527.     vtfreescr ((SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR));
  528.     break;
  529.     
  530.     case WM_KILLFOCUS:
  531.     EmacsCaret (FALSE);
  532.     goto DefaultProc;
  533.     case WM_SETFOCUS:
  534.     EmacsCaret (TRUE);
  535.     goto DefaultProc;
  536.     case WM_ACTIVATE:
  537.     if (wParam) EmacsCaret (TRUE);
  538.     goto DefaultProc;
  539.  
  540.     case WM_MDIACTIVATE:
  541.     if (wParam) {   /* this one is becoming active */
  542.         if (!InternalRequest) {
  543.             InternalRequest = TRUE;
  544.             select_screen ((SCREEN *) GetWindowLong (hWnd, GWL_SCRPTR),
  545.                                FALSE);
  546.         InternalRequest = FALSE;
  547.         }
  548.         else {
  549.         SCREEN  *sp;
  550.  
  551.         sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
  552.         if (sp->s_virtual == NULL) {
  553.             /* this is initialization time! */
  554.             vtinitscr (sp, DisplayableRows (hWnd, 0, &EmacsCM),
  555.                        DisplayableColumns (hWnd, 0, &EmacsCM));
  556.         }
  557.         }
  558.     }
  559.     goto DefaultProc;
  560.     
  561.     case WM_KEYDOWN:
  562.     case WM_SYSKEYDOWN:
  563.     case WM_CHAR:
  564.     case WM_SYSCHAR:
  565.     if (EatKey (wMsg, wParam, lParam)) break;
  566.         goto DefaultProc;
  567.  
  568.     case WM_MOUSEMOVE:
  569.     case WM_LBUTTONDOWN:
  570.     case WM_LBUTTONUP:
  571.     case WM_MBUTTONDOWN:
  572.     case WM_MBUTTONUP:
  573.     case WM_RBUTTONDOWN:
  574.     case WM_RBUTTONUP:
  575.     MouseMessage (hWnd, wMsg, wParam, lParam);
  576.     break;
  577.  
  578.     case WM_SETCURSOR:
  579.     if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
  580.     goto DefaultProc;
  581.  
  582.     case WM_PAINT:
  583.     ScrPaint (hWnd);
  584.     break;
  585.     
  586.     case WM_SIZE:
  587.     if (!IsIconic (hFrameWnd)) {
  588.         if (wParam == SIZEICONIC) EmacsCaret (FALSE);
  589.         ScrReSize (hWnd, wParam, LOWORD(lParam), HIWORD(lParam));
  590.     }
  591.     goto DefaultProc;
  592.  
  593.     case WM_GETMINMAXINFO:
  594.     GetMinMaxInfo (hWnd, (LPPOINT)lParam);
  595.     goto DefaultProc;
  596.     
  597.     case WM_MOUSEACTIVATE:
  598.     if (notquiescent) break;    /* blocks mouse selection of an MDI
  599.                        child's caption */
  600.     goto DefaultProc;
  601.  
  602.     case WM_SYSCOMMAND:
  603.     switch (wParam & 0xFFF0) {
  604.     case SC_RESTORE:
  605.     case SC_SIZE:
  606.     case SC_MAXIMIZE:
  607.     case SC_MINIMIZE:
  608.     case SC_NEXTWINDOW:
  609.     case SC_PREVWINDOW:
  610.         if (notquiescent) break;    /* sizing commands disabled */
  611.         goto DefaultProc;
  612.     case SC_CLOSE:
  613.         if (!notquiescent) {
  614.         SCREEN  *sp;
  615.  
  616.         /* this must be done here, before any MDI mumbo-jumbo */
  617.             sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
  618.         if (sp == first_screen) {
  619.             cycle_screens (FALSE, 0);
  620.                 }
  621.             if (sp != first_screen) {
  622.                 unlist_screen (sp);
  623.                     free_screen (sp);
  624.         }
  625.         update (TRUE);  /* to restore the caret */
  626.         }
  627.         break;
  628.     case SC_MOVE:
  629.         if (notquiescent) {
  630.             /* prevent moving an MDI client other than the already
  631.            active one, since it would transfer the activation */
  632.             if (hWnd != LOWORD(SendMessage (hMDIClientWnd,
  633.                         WM_MDIGETACTIVE,
  634.                         0, 0L))) break;
  635.         }
  636.         goto DefaultProc;
  637.     default:
  638.         goto DefaultProc;
  639.     }
  640.     break;
  641.  
  642.     default:
  643. DefaultProc:
  644.     return DefMDIChildProc (hWnd, wMsg, wParam, lParam);
  645.     }
  646.     return 0L;
  647. } /* ScrWndProc */
  648.  
  649. /* FrameWndProc:    frame window function */
  650. /* ============                           */
  651. LONG EXPORT far pascal FrameWndProc (HWND hWnd, WORD wMsg, WORD wParam,
  652.                      LONG lParam)
  653. {
  654.     switch (wMsg) {
  655.         
  656.     case WM_INITMENUPOPUP:
  657.     InitMenuPopup (wParam, lParam);
  658.     goto DefaultProc;
  659.  
  660.     case WM_MENUCHAR:
  661.     {
  662.         LONG    Code;
  663.         
  664.         Code = DefFrameProc (hWnd, hMDIClientWnd, wMsg, wParam, lParam);
  665.         if (HIWORD(Code) != 0) return Code; /* matches a menu command */
  666.         if (EatKey (wMsg, wParam, lParam)) return MAKELONG(0,1);
  667.             /* MicroEMACS ate that character, close the current menu! */
  668.         else return Code;
  669.     }
  670.  
  671.     case WM_COMMAND:
  672.     if (wParam == EMACS_BROADCAST) {
  673.         int     Id;
  674.  
  675.         switch (HIWORD(lParam)) {
  676.         case EMACS_STARTING:
  677.         /* another instance of emacs is starting. If our ID was
  678.            0, it should now be 1 */
  679.         if (GetWindowWord (hFrameWnd, GWW_FRMID) == 0) {
  680.             SetWindowWord (hFrameWnd, GWW_FRMID, 1);
  681.             SetFrameCaption ();
  682.         }
  683.         break;
  684.         case EMACS_ENDING:
  685.         /* another instance of emacs is ending. If we are now
  686.            alone, let's switch our Id back to 0 */
  687.         Id = EmacsBroadcast (0, LOWORD(lParam));
  688.         /* Id here is the number of Emacs applications, except
  689.            the dying one */
  690.         if (Id == 1) {
  691.             SetWindowWord (hFrameWnd, GWW_FRMID, 0);
  692.             SetFrameCaption ();
  693.         }
  694.         break;
  695.         }
  696.     }
  697.     else {
  698.         if (!MenuCommand (wParam, lParam)) goto DefaultProc;
  699.     }
  700.     break;
  701.  
  702.     case WM_SYSCOMMAND:
  703.     switch (wParam & 0xFFF0) {
  704.     case SC_RESTORE:
  705.         if (IsIconic (hFrameWnd)) goto DefaultProc;
  706.     case SC_SIZE:
  707.     case SC_MAXIMIZE:
  708.         if (notquiescent) break;    /* sizing commands disabled */
  709.         goto DefaultProc;
  710.     case SC_OPTIMIZE:
  711.         if (notquiescent) break;
  712.         if (IsZoomed (hFrameWnd) || IsIconic (hFrameWnd)) {
  713.             /* must restore it first */
  714.             ShowWindow (hFrameWnd, SW_RESTORE);
  715.         }
  716.         MoveWindow (hFrameWnd, 0, 0,
  717.                     GetSystemMetrics (SM_CXSCREEN),
  718.                     GetSystemMetrics (SM_CYSCREEN)
  719.                     - GetSystemMetrics (SM_CYICON)
  720.             - GetSystemMetrics (SM_CYCAPTION)
  721.                     - GetSystemMetrics (SM_CYBORDER),
  722.                     TRUE);
  723.         break;
  724.     default:
  725.         goto DefaultProc;
  726.     }
  727.     break;
  728.     
  729.     case WM_SETCURSOR:
  730.     if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
  731.     goto DefaultProc;
  732.  
  733.     case WM_TIMER:
  734.     if (wParam == T_SLEEP) {
  735.         --Sleeping;     /* signal wake up to TakeANap() */
  736.         break;
  737.     }
  738.     goto DefaultProc;
  739.     
  740.     case WM_PAINT:  /* must be the message line */
  741.     MLPaint ();
  742.     break;
  743.  
  744.     case WM_SIZE:   /* must adjust the MDIClient's size to fit the
  745.                Message Line */
  746.     MoveWindow (hMDIClientWnd, 0, 0,
  747.                     LOWORD(lParam), HIWORD(lParam) - EmacsCM.MLHeight, TRUE);
  748.     break;
  749.  
  750.     case WM_NCLBUTTONDBLCLK:
  751.     {   /* This corrects a Windows 3.0 bug that prevents a maximized
  752.            child from being closed by double-clicking the close-box.
  753.            See "Windows 3: A Developer's Guide" by Jeffrey M.
  754.            Richter, page 480 */
  755.         RECT    Rect;
  756.         HBITMAP hBmp;
  757.         BITMAP  Bmp;
  758.         DWORD   MDIGetActivateResult;
  759.         
  760.         if (wParam != HTMENU) goto DefaultProc;
  761.         MDIGetActivateResult = SendMessage (hMDIClientWnd,
  762.                         WM_MDIGETACTIVE, 0, 0);
  763.         if (!HIWORD(MDIGetActivateResult)) goto DefaultProc;
  764.         GetWindowRect (hWnd, &Rect);
  765.         Rect.top += GetSystemMetrics(SM_CYCAPTION) +
  766.             GetSystemMetrics (SM_CYFRAME);
  767.         Rect.left += GetSystemMetrics(SM_CXFRAME);
  768.         hBmp = LoadBitmap (NULL, MAKEINTRESOURCE(OBM_CLOSE));
  769.         GetObject (hBmp, sizeof(BITMAP), (LPSTR)(LPBITMAP)&Bmp);
  770.         Rect.bottom = Rect.top + Bmp.bmHeight;
  771.         Rect.right = Rect.left + Bmp.bmWidth / 2;
  772.         if (!PtInRect(&Rect, MAKEPOINT(lParam))) goto DefaultProc;
  773.         SendMessage(LOWORD(MDIGetActivateResult), WM_SYSCOMMAND,
  774.                         SC_CLOSE, lParam);
  775.     }  
  776.     break;
  777.  
  778.     case WM_ACTIVATE:
  779.     EmacsCaret (wParam);
  780.         /* This call matters only when the active MDI Child is
  781.            iconic, in which case it never receives the focus and
  782.            thus fails to create the caret (that should still appear
  783.            if we are getting input on the message line) */
  784.     goto DefaultProc;
  785.  
  786.     case WM_CREATE:
  787.     hFrameWnd = hWnd;
  788.     FrameInit ((CREATESTRUCT *)lParam);
  789.     goto DefaultProc;
  790.     
  791.     case WM_QUERYENDSESSION:
  792.     if (CloseEmacs (wMsg)) return 1L;
  793.     break;
  794.     
  795.     case WM_CLOSE:
  796.         if (CloseEmacs (wMsg)) goto DefaultProc;
  797.     break;
  798.     
  799.     case WM_DESTROY:
  800.     if (MainHelpUsed) WinHelp (hFrameWnd, MainHelpFile, HELP_QUIT, NULL);
  801.     if (HelpEngineFile[0] != '\0') WinHelp (hFrameWnd, HelpEngineFile,
  802.                                                 HELP_QUIT, NULL);
  803.     EmacsBroadcast (EMACS_ENDING, hFrameWnd);
  804.     PostQuitMessage (0);
  805.     break;
  806.     
  807.     default:
  808. DefaultProc:
  809.     return DefFrameProc (hWnd, hMDIClientWnd, wMsg, wParam, lParam);
  810.     }
  811.     return 0L;
  812. } /* FrameWndProc */
  813.  
  814. /* WinMain: Application entry point */
  815. /* =======                          */
  816.  
  817. int pascal  WinMain (HANDLE hInstance, HANDLE hPrevInstance,
  818.                      LPSTR lpCmdLine, int nCmdShow)
  819. {
  820.     hEmacsInstance = hInstance;
  821.     if (!WinInit (lpCmdLine, nCmdShow)) return -1;
  822.  
  823.     switch (Catch (ExitCatchBuf)) {
  824.     case 0:
  825.     emacs (argc, argv);
  826.     /* If we exit through an emacs command, we pass here. Otherwise
  827.        (exit through a close in system menu or a windows session
  828.        termination), the application terminates from the call to
  829.        mswgetc that received the signal */
  830.     case ABORT:
  831.         eexitflag = TRUE;
  832.         longop (FALSE);
  833.     PostMessage (hFrameWnd, WM_CLOSE, 0, 0L);
  834.     for (;;) MessageLoop (TRUE);/* go into message loop until WM_QUIT */
  835. /*  case TRUE:
  836.         break; */
  837.     }
  838.     return eexitval;
  839. } /* WinMain */
  840.  
  841. /* ModifyCursor:    forces a WM_SETCURSOR */
  842. /* ============                           */
  843.  
  844. static void pascal near ModifyCursor (void)
  845. {
  846.     POINT   pt;
  847.  
  848.     GetCursorPos (&pt);
  849.     SetCursorPos (pt.x, pt.y);
  850. } /* ModifyCursor */
  851.  
  852. /* MessageLoop: Main message loop */
  853. /* ===========                    */
  854.  
  855. static void near pascal MessageLoop (BOOL WaitMode)
  856.  
  857. /* If WaitMode is TRUE this function uses GetMessage the first time and
  858.    PeekMessage after that, until the input queue is empty and there is
  859.    something in the in_put pipe. Thus, WM_PAINTs are processed and the
  860.    input queue is emptied into the in_put pipe.
  861.    If WaitMode is FALSE, this function uses PeekMessage from the
  862.    beginning and returns only when the input queue is empty. In that
  863.    case, WM_PAINTs are not processed, but the processor still gets
  864.    relinquished. Note that while peeking for messages, this function
  865.    returns if the in_put pipe is about to overflow */
  866. {
  867.     MSG     Msg;
  868.     BOOL    Peeking;
  869.  
  870.     ++notquiescent; /* this restrics some actions in nested calls */
  871.     ModifyCursor ();
  872.  
  873.     do {
  874.         Peeking = !WaitMode;
  875.         for (;;) {
  876.             if(defferupdate) {
  877.                 ShowEmacsCaret (FALSE);
  878.                 update (TRUE);
  879.                 ShowEmacsCaret (TRUE);
  880.             }
  881.         if (Peeking) {
  882.             if (!in_room (5)) break; /* input stream about to overflow */
  883.             if (!PeekMessage (&Msg, NULL, 0, 0, PM_REMOVE)) break;
  884.         }
  885.         else {
  886.                 longop (FALSE); /* we are going to wait for input */
  887.             GetMessage (&Msg, NULL, 0, 0);
  888.             Peeking = TRUE; /* from now on... */
  889.         }
  890.         if (Msg.message == WM_QUIT) {   /* time to leave... */
  891.             JettisonFarStorage ();
  892.             if (hEmacsFont) DeleteObject (hEmacsFont);
  893.             Throw (ExitCatchBuf, TRUE);
  894.             /* we're gone out of business ! */
  895.             /* **************************** */
  896.         }
  897.         if (!TranslateMDISysAccel (hMDIClientWnd, &Msg)) {
  898.             TranslateMessage (&Msg);
  899.             DispatchMessage (&Msg);
  900.         }
  901.     }
  902.     } while (WaitMode && !in_check ()); /* keep waiting if pipe is empty */
  903.  
  904.     --notquiescent;
  905.     ModifyCursor ();
  906.     return;
  907. } /* MessageLoop */
  908.  
  909. /* GetInput: wait for user input (called by mswgetc from mswdrv.c) */
  910. /* ========                                                        */
  911.  
  912. /* The returned value is the next character from the input stream */
  913.  
  914. int far pascal GetInput (void)
  915. {
  916.     if (!in_check ()) {
  917.     ShowEmacsCaret (TRUE);
  918.     MessageLoop (TRUE);
  919.     ShowEmacsCaret (FALSE);
  920.     }
  921.     return in_get ();       /* return next character */
  922. } /* GetInput */
  923.  
  924. /* TakeANap:    put emacs to sleep for a few milliseconds */
  925. /* ========                                               */
  926.  
  927. int far pascal  TakeANap (int t)
  928. /* this function is used by mswsleep(). It returns TRUE unless the timer
  929.    could not be created. Note that for a null time, it simply
  930.    relinquishes the processor */
  931. {
  932.     if ((t == 0) ||
  933.         (!SetTimer (hFrameWnd, T_SLEEP, t, (FARPROC)NULL))) {
  934.     ShowEmacsCaret (TRUE);
  935.     MessageLoop (FALSE);    /* let's do one relinquish anyway */
  936.     ShowEmacsCaret (FALSE);
  937.     if (t == 0) return TRUE;
  938.         else return FALSE;
  939.     }
  940.     ShowEmacsCaret (TRUE);
  941.     Sleeping = 1;   /* this gets reset by WM_TIMER processing */
  942.     do {
  943.         MessageLoop (FALSE);
  944.         WaitMessage ();
  945.     } while (Sleeping > 0);
  946.     ShowEmacsCaret (FALSE);
  947.     KillTimer (hFrameWnd, T_SLEEP);
  948.     return TRUE;
  949. } /* TakeANap */
  950.  
  951. /* UpdateCursor:    sets the apropriate Emacs cursor shape */
  952. /* ============                                            */
  953.  
  954. static BOOL near pascal UpdateCursor (HWND hWnd, WORD wParam, DWORD lParam)
  955.  
  956. /* this function should be called on each WM_SETCURSOR message, to
  957.    display the appropriate cursor. It returns TRUE if all processing has
  958.    been done (the calling window function should return TRUE). it
  959.    returns FALSE if the default window proc sould be called. */
  960. {
  961.     HCURSOR hCursor;
  962.  
  963.     switch (LOWORD(lParam)) {   /* hit-test area code as in WM_NCHITTEST */
  964.     case HTCLIENT:
  965.         if (HourglassOn) hCursor = hHourglass;
  966.     else {
  967.         if (wParam == hFrameWnd) {
  968.             if (notquiescent) hCursor = hNotQuiescentCursor;
  969.             else hCursor = NULL;
  970.         }
  971.         else {  /* a screen window or the MDIClient */
  972.         if (notquiescent) hCursor = hNotQuiescentCursor;
  973.         else if (wParam == hMDIClientWnd) {
  974.             hCursor = NULL;
  975.         }
  976.         else if (mouseflag) {
  977.             if (MouseTracking) hCursor = hTrackCursor;
  978.             else hCursor = hScreenCursor;
  979.         }
  980.         else hCursor = 0;
  981.         }
  982.     }
  983.     if (!hCursor) hCursor = LoadCursor (NULL, IDC_ARROW);
  984.     break;
  985.     case HTHSCROLL:
  986.     case HTVSCROLL:
  987.     case HTREDUCE:
  988.         if ((wParam == hMDIClientWnd) || (wParam == hFrameWnd)) return FALSE;
  989.     case HTBOTTOM:
  990.     case HTBOTTOMLEFT:
  991.     case HTBOTTOMRIGHT:
  992.     case HTLEFT:
  993.     case HTRIGHT:
  994.     case HTTOP:
  995.     case HTTOPLEFT:
  996.     case HTTOPRIGHT:
  997.     case HTSIZE:
  998.     case HTZOOM:
  999.         /* all those are unuseable when notquiescent */
  1000.         if (notquiescent) {
  1001.             if (HourglassOn) hCursor = hHourglass;
  1002.         else hCursor = hNotQuiescentCursor;
  1003.         }
  1004.         else return FALSE;
  1005.     break;
  1006.     default:
  1007.     return FALSE;
  1008.     }
  1009.     SetCursor (hCursor);
  1010.     return TRUE;
  1011. } /* UpdateCursor */
  1012.  
  1013. /* SetHourglass:    sets or removes the hourglass cursor */
  1014. /* ============                                          */
  1015.  
  1016. static void near pascal SetHourglass (BOOL hg)
  1017.  
  1018. /* hg = TRUE sets the hourglass, hg = FALSE removes it */
  1019. {
  1020.     POINT   Point;
  1021.     HWND    hWnd;
  1022.     
  1023.     if (hg == HourglassOn) return;   /* nothing to do */
  1024.     HourglassOn = hg;
  1025.     GetCursorPos (&Point);
  1026.     hWnd = WindowFromPoint (Point);
  1027.     if (hWnd && (GetWindowTask (hWnd) == GetCurrentTask ())) {
  1028.         /* the cursor is above one of MicroEMACS's windows */
  1029.     SetCursorPos (Point.x, Point.y);
  1030.     /* this should cause a dummy cursor movement to be processed,
  1031.        ending up in UpdateCursor being invoked */
  1032.     }
  1033. } /* SetHourglass */
  1034.